#!/bin/sh
set -e
#================================================================================================================#
# 在使用前，请确保 crul、openssl、nslookup
# 若未安装，则 yum install -y crul openssl bind-utils
#================================================================================================================#
# 功能：用于更新阿里云域名解析，实现DDNS功能
#
# --yaN-jiA-haO--, modified 2023/05/20
# 在 ESXI 手动执行/定时任务(crontab)执行测试通过
# 2021-11-05 修改 支持@ 和*域名的处理，增加对群晖6.22的bug的修改并测试通过。
# 2021-11-05 增加日志功能。
# 2022-11-11 修复nslookup在esxi中dns获取不准确
# 2022-11-11 增加域名多条记录更新
# 2023-05-20 修复多条域名更新部分无法更新，及配色
#================================================================================================================#
# 使用方法：
#
# 方法1. 内部参数
# 
# 示例: 
# aliddns_ak="xxxxxxx"
# aliddns_sk="xxxxxxx"
# aliddns_subdomain="test,test2,test3"
# aliddns_domain="mydomain.site"
# aliddns_iptype="A"
# aliddns_ttl=600 
# 执行：Ali_DDNS.sh
#================================================================================================================#

#--------------------------------------------------------------
# 参数
#
# (*)阿里云 AccessKeyId 
aliddns_ak='LTAI5tNnUAnFQk9vZJms72qf'
# (*)阿里云 AccessKeySecret 
aliddns_sk='bRt3XPjjm6Cv1B4FdxtTFjKHpi2gVZ'

# (*)域名：test.mydomain.com 
aliddns_subdomain_all='@,www,frp' #'test'
aliddns_domain='gitool.cn' #'mydomain.com'

# (*)ip地址类型：'A' 或 'AAAA'，代表ipv4 和 ipv6
aliddns_iptype='A' # 'A' 或 'AAAA'，代表ipv4 和 ipv6

# TTL 默认10分钟 = 600秒 
aliddns_ttl=600 #"600"

#--------------------------------------------------------------
mdate=$(date "+%Y-%m-%d")
logPath="/tmp/aliddns-update.log"
debug_flag="0"
echo '' > $logPath
#--------------------------------------------------------------
machine_ip=""
ddns_ip=""
aliddns_record_id=""
#--------------------------------------------------------------

IFS=','
array=($aliddns_subdomain_all)
for aliddns_subdomain in ${array[@]}
do
    # 判断主机记录
    if [ "$aliddns_subdomain" = "@" ]; then
    	aliddns_subdomain="%40"
    	aliddns_name=$aliddns_domain
    elif [ "$aliddns_subdomain" = '*' ]; then
    	aliddns_subdomain="%2A"	
    else
        aliddns_name=$aliddns_subdomain.$aliddns_domain
    fi

    echo -e "********** \033[34m$(date +"%Y-%m-%d %H:%M:%S")\033[0m \033[31mSTART\033[0m **********"  | tee -a "$logPath"

    # https://ipinfo.io/ip http://icanhazip.com/
    function getMachine_IPv4() {
        local ipv4
        ipv4=`curl -s 4.ipw.cn`
        echo "$ipv4"
    }

    function getMachine_IPv6() {
        local ipv6
        ipv6=$(ip addr | grep "inet6.*global" | grep -v "deprecated" | grep "dynamic" | awk '{print $2}' | awk -F"/" '{print $1}')
        echo "$ipv6"
    }
    # 需要安装一下nslookup命令，包名：bind-utils
    function getDDNS_IPV4() {
        current_ip=$(nslookup -query=$aliddns_iptype $aliddns_name | grep "Address" | grep -v "#53" | awk '{print $2}' | awk 'NR==2')
        echo "$current_ip"
    }

    function getDDNS_IPV6() {
        current_ip=$(nslookup -query=$aliddns_iptype ${aliddns_name/\*/whoami2021} | grep "address" | awk '{print $5}' | awk 'NR==2')
        echo "$current_ip"
    }

    function enc() {
        echo -n "$1" | urlencode
    }

    function send_request() {
        local args="AccessKeyId=$aliddns_ak&Action=$1&Format=json&$2&Version=2015-01-09"
        local  hash
        hash=$(echo -n "GET&%2F&$(enc "$args")" | openssl dgst -sha1 -hmac "$aliddns_sk&" -binary | openssl base64) 
        if [ "$debug_flag" = '1' ]; then echo "debug info [send_request]:(args) $args" | tee -a "$logPath" >&2  ; fi
        curl -s "http://alidns.aliyuncs.com/?$args&Signature=$(enc "$hash")"
    }

    function get_recordid() {
      grep -Eo '"RecordId":"[0-9]+"' | cut -d':' -f2 | tr -d '"'
    }

    function query_recordid() {	
    	local result
        result=$(send_request "DescribeSubDomainRecords" "SignatureMethod=HMAC-SHA1&SignatureNonce=$timestamp&SignatureVersion=1.0&SubDomain=$aliddns_subdomain.$aliddns_domain&Timestamp=$timestamp&Type=$aliddns_iptype")
    	if [ "$debug_flag" = '1' ]; then echo "debug info [query_recordid]: $result" | tee -a "$logPath" >&2  ; fi
    	echo "$result"
    }

    function update_record() {
        send_request "UpdateDomainRecord" "RR=$aliddns_subdomain&RecordId=$1&SignatureMethod=HMAC-SHA1&SignatureNonce=$timestamp&SignatureVersion=1.0&TTL=$aliddns_ttl&Timestamp=$timestamp&Type=$aliddns_iptype&Value=$(enc $machine_ip)"
    }

    function add_record() {
        send_request "AddDomainRecord&DomainName=$aliddns_domain" "RR=$aliddns_subdomain&SignatureMethod=HMAC-SHA1&SignatureNonce=$timestamp&SignatureVersion=1.0&TTL=$aliddns_ttl&Timestamp=$timestamp&Type=$aliddns_iptype&Value=$(enc $machine_ip)"
        }

    function urlencode() {
        # urlencode <string>
        out=""
        while read -r -n1 c
        do
            case $c in
                [a-zA-Z0-9.~_-]) out="$out$c" ;;
                *) out="$out$(printf '%%%02X' "'$c")" 
                ;;
            esac
        done
        if [ "$debug_flag" = '1' ]; then echo "debug info [enc]:(urlencode) $out " | tee -a "$logPath" >&2 ; fi
        
        echo -n "$out"
        }
        
    
    function start_update() {
        #echo "start update..." | tee -a "$logPath"
        timestamp=$(date -u "+%Y-%m-%dT%H%%3A%M%%3A%SZ")     
        aliddns_record_id=$(query_recordid | get_recordid) 
        # add support */%2A and @/%40 record
        if [ "$aliddns_record_id" = "" ] && [  "$ddns_ip" = "" ]; then
            echo "add record starting"  | tee -a "$logPath"

            aliddns_record_id=$(add_record | get_recordid)

            if [ "$aliddns_record_id" = "" ]
            then
                echo "aliddns_record_id is empty !"  | tee -a "$logPath"
            else
                echo "added record $aliddns_record_id "  | tee -a "$logPath"
            fi
        elif [ ! "$aliddns_record_id" = "" ]; then
            #echo "update record starting"  | tee -a "$logPath"
            
            update_record "$aliddns_record_id" &>/dev/null
            
            echo -e "\033[34mDNS RecordID\033[0m    \033[44m==>\033[0m     \033[96m$aliddns_record_id\033[0m"  | tee -a "$logPath"
        else
            echo -e "\033[41mUpdated DNS Record ID error ! ==>    $aliddns_record_id\033[0m"  | tee -a "$logPath"
        fi
        echo -e "********** \033[34m$(date +"%Y-%m-%d %H:%M:%S")\033[0m \033[32mEND\033[0m ************\n"  | tee -a "$logPath"
    }    

    if [ "$aliddns_iptype" = 'A' ]; then
        IPtype="IPv4"
        ddns_ip="$(getDDNS_IPV4)"
        machine_ip="$(getMachine_IPv4)"
        aliddns_record_id=$aliddnsipv4_record_id
    else
        IPtype="IPv6"
        ddns_ip="$(getDDNS_IPV6)"
        machine_ip=`curl -s 6.ipw.cn`
        aliddns_record_id=$aliddnsipv6_record_id
    fi

    if [ "$machine_ip" = "" ]; then
        echo "machine_ip is empty!" | tee -a "$logPath"
        exit 0
    fi

    if [ "$machine_ip" = "$ddns_ip" ]; then
        echo -e "\033[33m  $aliddns_name\033[0m \033[96mdidn't make any changes...\033[0m" | tee -a "$logPath"
        echo -e "********** \033[34m$(date +"%Y-%m-%d %H:%M:%S")\033[0m \033[32mEND\033[0m ************\n"  | tee -a "$logPath"
        # if [ "$debug_flag" = '0' ]; then exit 1 ; fi
    else
        echo -e "\033[34mDomain name\033[0m     \033[44m==>\033[0m     \033[96m$aliddns_name\033[0m" | tee -a "$logPath"
        echo -e "\033[34mIP type\033[0m         \033[44m==>\033[0m     \033[96m$IPtype\033[0m" | tee -a "$logPath"
        echo -e "\033[34mCurrent IP\033[0m      \033[44m==>\033[0m     \033[96m$ddns_ip\033[0m" | tee -a "$logPath"
        echo -e "\033[34mUpdate  IP\033[0m      \033[44m==>\033[0m     \033[96m$machine_ip\033[0m" | tee -a "$logPath"
        start_update
    fi
    
done
